package com.hrsaas.ext.spider.core;

import java.util.function.Consumer;

/**
 * Callback which can be passed some object which cannot be computed on the
 * calling thread.
 * <p>
 * Typically the object which is passed to a Receiver is not meant to be touched
 * except from within the receive() method; implementations may proxy an
 * interface to one which will check that the correct locks are held and throw
 * an exception otherwise.
 * </p><p>
 * This class is superseded by JDK 8's java.util.function.Consumer - prefer
 * that. An adapter method is provided. It will be deprecated soon.
 * </p>
 *
 * @author Tim Boudreau
 */
public abstract class Receiver<T> implements Consumer<T> {

    public abstract void receive(T object);

    public final void accept(T object) {
        receive(object);
    }

    public <E extends Throwable> void onFail(E exception) throws E {
        throw exception;
    }

    public void onFail() {
        //do nothing
        System.err.println(this + " failed");
    }

    public static <T> Receiver<T> of(Callback<T> callback) {
        return new CallbackReceiver<>(callback);
    }

    public static <T> Receiver<T> of(Consumer<T> cons) {
        return new Receiver<T>() {
            @Override
            public void receive(T object) {
                cons.accept(object);
            }
        };
    }

    private static final class CallbackReceiver<T> extends Receiver<T> {

        private final Callback<T> callback;

        public CallbackReceiver(Callback<T> callback) {
            this.callback = callback;
        }

        @Override
        public void receive(T object) {
            callback.receive(null, object);
        }

        @Override
        public <E extends Throwable> void onFail(E exception) throws E {
            callback.receive(exception, null);
        }

        @Override
        public void onFail() {
            callback.receive(new Exception("Unknown failure"), null);
        }
    }
}
